﻿using System;
using System.Runtime.Serialization;
using System.Security;
using System.Security.Permissions;

namespace vm.Aspects.Policies.Tests
{
    [Serializable]
    public class TestPermission : IPermission, IUnrestrictedPermission, ISerializable, IEquatable<TestPermission>
    {
        bool _unrestricted;
        bool _permitted = true;

        public TestPermission()
        {
            _permitted = true;
        }

        public TestPermission(PermissionState state, bool permitted)
        {
            switch (state)
            {
            case PermissionState.None:
                _permitted = permitted;
                break;

            case PermissionState.Unrestricted:
                _unrestricted = true;
                _permitted = true;
                break;
            }
        }

        public void Demand()
        {
            if (_unrestricted || _permitted)
                return;
            throw new SecurityException("Access denied.");
        }

        public IPermission Copy()
        {
            return new TestPermission
            {
                _unrestricted = this._unrestricted,
                _permitted    = this._permitted,
            };
        }

        public IPermission Intersect(IPermission target)
        {
            var t = target as TestPermission;

            if (t == null)
                return null;

            if (t.IsSubsetOf(this))
                return t.Copy();

            if (this.IsSubsetOf(t))
                return this.Copy();

            return null;
        }

        public bool IsSubsetOf(IPermission target)
        {
            var t = target as TestPermission;

            if (t == null)
                return false;

            if (_unrestricted || t._unrestricted)
                return false;

            return _permitted == t._permitted;
        }

        public IPermission Union(IPermission other)
        {
            var t = other as TestPermission;

            if (t == null)
                return null;

            if (t.IsSubsetOf(this))
                return this.Copy();

            if (this.IsSubsetOf(t))
                return t.Copy();

            return null;
        }

        public SecurityElement ToXml()
        {
            SecurityElement esd = new SecurityElement("IPermission");

            esd.AddAttribute("class", typeof(TestPermission).AssemblyQualifiedName);
            esd.AddAttribute("version", "1.0");

            if (_unrestricted)
                esd.AddAttribute("Unrestricted", true.ToString());

            if (_permitted)
                esd.AddAttribute("Permitted", true.ToString());
            return esd;
        }

        public void FromXml(SecurityElement elem)
        {
            if (elem == null)
                throw new ArgumentNullException("elem");

            string value = elem.Attribute("Unrestricted");

            _unrestricted = value!=null ? bool.Parse(value) : false;

            value = elem.Attribute("Permitted");

            _permitted = value!=null ? bool.Parse(value) : false;
        }

        #region IUnrestrictedPermission Members

        public bool IsUnrestricted()
        {
            return _unrestricted;
        }

        #endregion

        #region Identity rules implementation.
        #region IEquatable<TestPermission> Members
        public virtual bool Equals(TestPermission other)
        {
            if (ReferenceEquals(other, null))
                return false;
            if (ReferenceEquals(this, other))
                return true;

            return _permitted    == other._permitted &&
                   _unrestricted == other._unrestricted;
        }
        #endregion

        public override bool Equals(object obj)
        {
            return Equals(obj as TestPermission);
        }

        public override int GetHashCode()
        {
            var hashCode = Constants.HashInitializer;

            hashCode = Constants.HashMultiplier * hashCode + _permitted.GetHashCode();
            hashCode = Constants.HashMultiplier * hashCode + _unrestricted.GetHashCode();

            return hashCode;
        }

        public static bool operator==(
            TestPermission left,
            TestPermission right)
        {
            return ReferenceEquals(left, null) 
                        ? ReferenceEquals(right, null) 
                        : left.Equals(right);
        }

        public static bool operator!=(
            TestPermission left,
            TestPermission right)
        {
            return !(left==right);
        }
        #endregion

        #region ISerializable Members

        private TestPermission(SerializationInfo info, StreamingContext context)
        {
            if (info == null)
                throw new ArgumentNullException("info");

            _permitted    = info.GetBoolean("Permitted");
            _unrestricted = info.GetBoolean("Unrestricted");
        }

        public void GetObjectData(
            SerializationInfo info,
            StreamingContext context)
        {
            if (info == null)
                throw new ArgumentNullException("info");

            info.AddValue("Permitted", _permitted);
            info.AddValue("Unrestricted", _unrestricted);
        }

        #endregion
    }
}
